Public Class frmQLearning
    Dim matrizCaminos(,,) As Integer
    Dim Resultados As ArrayList
    Dim matrizR(,) As Decimal
    Dim matrizQ(,) As Decimal
    Dim aprendizaje As Decimal
    Dim episodios As Integer
    Dim cantCiudades As Integer
    Dim vMax As Integer
    Dim matrizAMostrar(,,) As Integer
    Dim matrizPuntosCiudades(,) As Integer
    Dim ciudadDestino As Integer

    Public minuto As Integer = 0
    Public segundo As Integer = 0
    Public milisegundo As Integer = 0

    Public Sub New(ByVal matriz)
        Me.InitializeComponent()
        Me.matrizCaminos = matriz
        Me.cantCiudades = Me.matrizCaminos.GetUpperBound(0) + 1

        For i As Integer = 1 To Me.cantCiudades
            Me.cmbCiudadDestino.Items.Add(i)
        Next
        Me.cmbCiudadDestino.SelectedIndex = 0
    End Sub

    Private Sub btnReset_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnReset.Click
        Me.cmbCiudadDestino.SelectedIndex = 0
        Me.nudParametro.Value = 0.95
        Me.nupEpisodios.Value = 1000
        Me.pbGlobal.Value = 0
        Me.dgvQ.DataSource = ""
        Me.dgvR.DataSource = ""
        Me.dgvResultados.DataSource = ""
    End Sub

    ' controlador del evento que se da cuando se preciona el boton generar

    Private Sub btgGenerar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btgGenerar.Click
        Dim aprendizaje As Decimal = Me.nudParametro.Value
        Dim episodios As Integer = Me.nupEpisodios.Value
        Dim estadoObjetivo As Integer = Me.cmbCiudadDestino.SelectedItem
        Me.ciudadDestino = estadoObjetivo
        ' instanciamos la clase Q learning con las parametros correspondientes introducidos por el usuario
        Dim ql As New QLearning(aprendizaje, episodios, estadoObjetivo, Me.matrizCaminos, Me.pbGlobal)

        'Se arranca el contador
        Dim count As New System.Diagnostics.Stopwatch()
        count.Start()
        'se llama al metodo inicial del objeto QLearning instanciado
        ql.Iniciar()

        count.Stop()

        'Determino la duracion
        Me.milisegundo = count.ElapsedMilliseconds Mod 1000
        Me.segundo = (count.ElapsedMilliseconds \ 1000) Mod 60
        Me.minuto = (count.ElapsedMilliseconds \ 1000) \ 60

        Me.txbMin.Text = Me.minuto
        Me.txbSeg.Text = Me.segundo
        Me.txbMilSeg.Text = Me.milisegundo

        Me.Resultados = ql.Soluciones
        Me.matrizQ = ql.Q
        Me.matrizR = ql.R

        Me.calcularMaximo()
        Me.mostrarMatrizQ()
        Me.mostrarMatrizR()
        Me.MostrarResultadosQL()
        Me.generarListaCaminos()
    End Sub

    Private Sub mostrarMatrizQ()
        Dim dtQ As New DataTable()
        Dim dr As DataRow
        Dim i As Integer
        Dim j As Integer

        'Genero los encabezados de las columnas
        For i = 0 To Me.matrizQ.GetUpperBound(0)
            dtQ.Columns.Add(i + 1)
        Next

        'Cargo el DataTable con los valores de los caminos
        For i = 0 To Me.matrizQ.GetUpperBound(0)
            dr = dtQ.NewRow()
            For j = 0 To Me.matrizQ.GetUpperBound(1)
                dr.Item(j) = Me.matrizQ(i, j).ToString
            Next
            dtQ.Rows.Add(dr)
        Next

        'Paso el datatable de los caminos al datagridview
        Me.dgvQ.DataSource = dtQ

        'Genero los encabezados de las filas
        For i = 0 To Me.matrizQ.GetUpperBound(0)
            Me.dgvQ.Rows(i).HeaderCell.Value = (i + 1).ToString()
        Next
        Me.dgvQ.Refresh()
        Me.Refresh()
    End Sub

    Private Sub mostrarMatrizR()
        Dim dtR As New DataTable()
        Dim dr As DataRow
        Dim i As Integer
        Dim j As Integer

        'Genero los encabezados de las columnas
        For i = 0 To Me.matrizR.GetUpperBound(0)
            dtR.Columns.Add(i + 1)
        Next

        'Cargo el DataTable con los valores de los caminos
        For i = 0 To Me.matrizR.GetUpperBound(0)
            dr = dtR.NewRow()
            For j = 0 To Me.matrizR.GetUpperBound(1)
                If Me.matrizR(i, j) <> -100 Then
                    dr.Item(j) = Me.matrizR(i, j).ToString
                End If
            Next
            dtR.Rows.Add(dr)
        Next

        'Paso el datatable de los caminos al datagridview
        Me.dgvR.DataSource = dtR

        'Genero los encabezados de las filas
        For i = 0 To Me.matrizR.GetUpperBound(0)
            Me.dgvR.Rows(i).HeaderCell.Value = (i + 1).ToString()
        Next
        Me.dgvR.Update()
    End Sub

    Private Sub MostrarResultadosQL()
        'Paso la poblacion al DataGridView
        Dim dtResultado As New DataTable()
        Dim dr As DataRow
        'Dim cantResultados = Me.qlResult.Count - 1

        'Le pongo nombre a las columnas
        For i As Integer = 1 To cantCiudades
            dtResultado.Columns.Add("P" & i)
        Next
        dtResultado.Columns.Add("Aptitud")

        For Each x As ArrayList In Me.Resultados
            dr = dtResultado.NewRow()
            For i As Integer = 0 To cantCiudades - 1
                If i < x.Count Then
                    dr.Item(i) = x(i)
                End If
            Next
            dr.Item(cantCiudades) = Me.calcularAptitud(x)
            dtResultado.Rows.Add(dr)
        Next
        'Cargo la poblacion al DataGrid
        Me.dgvResultados.DataSource = ""
        Me.dgvResultados.DataSource = dtResultado
    End Sub

    Function calcularAptitud(ByVal camino As ArrayList)
        Dim aptitud As Decimal = Me.vMax
        Dim anterior As Integer = camino(0)
        For i As Integer = 1 To camino.Count - 1
            Dim actual As Integer = camino(i)
            Dim distancia As Integer = Me.MatrizCaminos(anterior - 1, actual - 1, 0)
            Dim tipo As Integer = Me.MatrizCaminos(anterior - 1, actual - 1, 1)
            Dim costo As Decimal = distancia / tipo
            aptitud -= costo
            anterior = actual
        Next
        Return aptitud
    End Function

    Sub calcularMaximo()
        Dim distanciaMax As Integer = 0
        For i As Integer = 0 To Me.matrizCaminos.GetUpperBound(0)
            For j As Integer = i To Me.matrizCaminos.GetUpperBound(1)
                If Me.matrizCaminos(i, j, 0) > distanciaMax Then
                    distanciaMax = Me.matrizCaminos(i, j, 0)
                End If
            Next
        Next
        Me.vMax = distanciaMax * Me.cantCiudades
    End Sub

    Sub calcularMaximo2()
        Dim costoTramo As Decimal
        Dim peorTramo As Integer = 0
        For i As Integer = 0 To Me.matrizCaminos.GetUpperBound(0)
            For j As Integer = i To Me.matrizCaminos.GetUpperBound(1)
                If Me.matrizCaminos(i, j, 0) <> 0 Then
                    costoTramo = Me.matrizCaminos(i, j, 0) / Me.matrizCaminos(i, j, 1)
                    If costoTramo > peorTramo Then
                        peorTramo = costoTramo
                    End If
                End If
            Next
        Next
        Me.vMax = peorTramo * Me.cantCiudades
    End Sub

    Private Sub panMapas_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles panMapas.Paint
        Try
            Dim probando As Integer = Me.matrizAMostrar.Length

            Me.matrizPuntosCiudades = Disposicion.CalcularPuntos(Me.panMapas.Width, Me.panMapas.Height)

            PuntosEstaticos.cargarPuntosDinamicos(Me.matrizPuntosCiudades)
            Ciudad.crearCiudades(Me.matrizAMostrar.GetUpperBound(0))
            Camino.crearCaminos(Me.matrizAMostrar)

            Dim i As Integer
            e.Graphics.Clear(Color.White)
            For i = 0 To Camino.caminos.Count - 1
                Camino.caminos(i).dibujar(e.Graphics)
            Next
            For i = 0 To Camino.caminos.Count - 1
                Camino.caminos(i).dibujarDistancias(e.Graphics)
            Next
            For i = 0 To Ciudad.ciudades.Count - 1
                Ciudad.ciudades(i).dibujar(e.Graphics)
            Next
        Catch
        End Try
    End Sub

    Sub generarMatrizAMostrar(ByVal origen As Integer)
        Try
            'Borro la matriz a mostrar
            ReDim Me.matrizAMostrar(Me.matrizCaminos.GetUpperBound(0), Me.matrizCaminos.GetUpperBound(1), 1)

            For Each x As ArrayList In Me.resultados
                If x(0) = origen Then
                    Dim anterior As Integer = origen
                    For i As Integer = 1 To x.Count - 1
                        Dim actual As Integer = x(i)
                        'Copio el tramo de la matrizcaminos a la matriz a mostrar
                        Me.matrizAMostrar(anterior - 1, actual - 1, 0) = Me.matrizCaminos(anterior - 1, actual - 1, 0)
                        Me.matrizAMostrar(anterior - 1, actual - 1, 1) = Me.matrizCaminos(anterior - 1, actual - 1, 1)

                        Me.matrizAMostrar(actual - 1, anterior - 1, 0) = Me.matrizCaminos(actual - 1, anterior - 1, 0)
                        Me.matrizAMostrar(actual - 1, anterior - 1, 1) = Me.matrizCaminos(actual - 1, anterior - 1, 1)

                        anterior = actual
                    Next
                    Exit Sub
                End If
            Next
        Catch

        End Try
    End Sub

    Private Sub generarListaCaminos()
        Me.cmbListaOrigenes.Items.Clear()
        Me.cmbListaOrigenes.Items.Add("Todos")
        For Each pos As ArrayList In Me.Resultados
            Dim origen As Integer = pos(0)
            Me.cmbListaOrigenes.Items.Add(origen)
        Next
        Me.cmbListaOrigenes.SelectedIndex = 0
        Me.lblDestino.Text = "Destino: Ciudad " + Me.ciudadDestino.ToString()
    End Sub

    Private Sub cmbListaOrigenes_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmbListaOrigenes.SelectedIndexChanged
        Dim origen As String = Me.cmbListaOrigenes.SelectedItem
        If origen = "Todos" Then
            Me.matrizAMostrar = Me.matrizCaminos.Clone()
            Me.lblDistancia.Text = "Distancia:"
        Else
            Me.generarMatrizAMostrar(origen)
            Dim distancia As Integer = Me.calcularDistancia(origen)
            Me.lblDistancia.Text = "Distancia: " + distancia.ToString() + " Km"
        End If
        Me.panMapas.Refresh()
    End Sub

    Function calcularDistancia(ByVal origen As Integer) As Integer
        Dim camino As New ArrayList()
        For Each x As ArrayList In Me.Resultados
            If x(0) = origen Then
                camino = x
                Exit For
            End If
        Next
        Dim distancia As Integer = 0
        Dim anterior As Integer = camino(0)
        Dim actual As Integer
        Dim dTramo As Integer

        For i As Integer = 1 To camino.Count - 1
            actual = camino(i)
            dTramo = Me.matrizCaminos(anterior - 1, actual - 1, 0)
            distancia += dTramo
            anterior = actual
        Next
        Return distancia
    End Function
End Class